use std::fs;
use std::io::{Read, Write};
use std::net::TcpStream;

use log::{debug, error, info, trace};
use serde::{Deserialize, Serialize};

use crate::http::request::{HttpRequest, Method};
use crate::http::response::HttpResponse;
use crate::smtp::auth::Credential;
use crate::smtp::mail::Mail;
use crate::smtp::sender::Mailer;
use crate::utils::*;

///总的处理函数，=,把请求放到对应的handler上
pub fn http_handler(mut client_socket: TcpStream) -> Result<(), Error> {
    let mut buffer = [0; 1024];
    client_socket.read(&mut buffer)?;
    //接受请求并序列化
    let req = HttpRequest::from_string(match String::from_utf8(buffer.to_vec()) {
        Ok(s) => { s }
        Err(_) => { return Err(Error::HttpError(HttpError::UnrecognizedRequest)); }
    })?;
    debug!("Http Request:\n{:?}", req);
    match req.method {
        Method::GET => {
            get_handle(client_socket, req.path)
        }
        Method::POST => {
            post_handle(client_socket, req.body)
        }
        Method::Others => {
            debug!("404 not Found");
            client_socket.write(HttpResponse::new().set_code("404").generate().as_bytes())?;
            Err(Error::HttpError(HttpError::MethodNotSupported))
        }
    }
}

//处理get请求
fn get_handle(mut client_socket: TcpStream, path: String) -> Result<(), Error> {
    let full_path = path.clone();
    let mut res = HttpResponse::new();
    let path: Vec<&str> = path.split("/").collect();
    //相当于一个小小的路由，太复杂的懒得写了，一共就一个页面
    match path[1] {
        //index页面
        "" => {
            let body = match fs::read_to_string("./public/index.html") {
                Ok(f) => { f }
                Err(e) => {
                    error!("{}",e);
                    client_socket.write(HttpResponse::new().set_code("500").generate().as_bytes())?;
                    return Err(Error::InternalError);
                }
            };
            res = res.set_body(body).set_code("200").add_header("Content-type", "text/html");
        }
        //静态资源路径
        _path => {
            trace!("Get Path:{}",format!("./public{}",full_path));
            match fs::read_to_string(format!("./public{}", full_path)) {
                Ok(f) => {
                    if full_path.ends_with(".css") {
                        res = res.add_header("Content-type", "text/css").set_body(f).set_code("200");
                    } else if full_path.ends_with("js") {
                        res = res.add_header("Content-type", "text/javascript").set_body(f).set_code("200");
                    } else if full_path.ends_with(".png") {
                        res = res.add_header("Content-type", "image/png").set_body(f).set_code("200");
                    }else if full_path.ends_with(".jpg") {
                        res = res.add_header("Content-type", "image/jpg").set_body(f).set_code("200");
                    }else if full_path.ends_with(".jpeg") {
                        res = res.add_header("Content-type", "image/jpeg").set_body(f).set_code("200");
                    }
                }
                Err(_) => { res = res.set_code("404"); }
            };
        }
    };
    client_socket.write(res.generate().as_bytes())?;
    Ok(())
}

#[derive(Debug, Deserialize)]
struct MailRequest {
    username: String,
    password: String,
    subject: String,
    receiver: String,
    content: String,
}

fn post_handle(mut client_socket: TcpStream, body: String) -> Result<(), Error> {
    debug!("POST DATA:{}",body);
    let mut res_body = JRes {
        msg: "Success".to_string(),
    };
    let mail_info: MailRequest = match serde_json::from_str(&body) {
        Ok(j) => { j }
        Err(_) => {
            client_socket.write(HttpResponse::new().set_code("500")
                .set_body(serde_json::to_string(&JRes { msg: "请求格式错误".to_string() }).unwrap())
                .generate().as_bytes())?;
            return Err(Error::HttpError(HttpError::RequestFormatError));
        }
    };
    info!("Got Mail Request:\n");
    info!("username: {},",mail_info.username);
    info!("subject: {},",mail_info.subject);
    info!("receiver: {},",mail_info.receiver);
    info!("content: {},",mail_info.content);

    match send_mail(mail_info) {
        Ok(_) => {}
        Err(e) => {
            match e {
                Error::SmtpError(e) => {
                    res_body.msg = match e {
                        SmtpError::AuthError => { "登录验证失败".to_string() }
                        SmtpError::InvalidAddress => { "未找到目标服务器".to_string() }
                        SmtpError::InvalidInput => { "不合法的输入参数".to_string() }
                        SmtpError::EHLO => { "EHLO失败".to_string() }
                        SmtpError::MailFrom => { "MAIL FROM失败".to_string() }
                        SmtpError::RcptTo(s) => { format!("地址为{}的地址邮寄失败.", s) }
                        SmtpError::Data => { "邮件发送失败".to_string() }
                    }
                }
                Error::InternalError => {
                    client_socket.write(HttpResponse::new().set_code("500").generate().as_bytes())?;
                }
                Error::ConnectionError(_) => { res_body.msg = "网络连接错误，请稍后再试。".to_string() }
                _ => {}
            }
        }
    }
    let res = HttpResponse::new().set_code("200")
        .set_body(serde_json::to_string(&res_body).unwrap())
        .add_header("Content-type", "application/json");
    client_socket.write(res.generate().as_bytes())?;
    Ok(())
}

#[derive(Serialize, Debug)]
struct JRes {
    msg: String,
}

fn send_mail(req: MailRequest) -> Result<(), Error> {
    let sender = &req.username;
    let receiver = &req.receiver;
    let mail = Mail::new(&req.subject, &req.content, sender, receiver);
    let credit = Credential::new(&req.username, &req.password);
    let server_addr: Vec<&str> = req.username.split("@").collect();
    let addr = if server_addr.len() == 2 && server_addr[1].len() > 0 {
        format!("smtp.{}", server_addr[1])
    } else {
        return Err(Error::SmtpError(SmtpError::InvalidAddress));
    };
    Mailer::new()
        .set_server_address(&addr)
        .set_port(25)
        .set_sender(sender)
        .add_receiver(receiver)
        .auth(credit)
        .set_mail(mail)
        .send()
}